home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / FLTK-1.0.6 / src / Fl.cxx < prev    next >
Encoding:
C/C++ Source or Header  |  1999-08-22  |  18.3 KB  |  705 lines

  1. #include <stdio.h>
  2. //
  3. // "$Id: Fl.cxx,v 1.24.2.12 1999/08/22 23:31:21 gustavo Exp $"
  4. //
  5. // Main event handling code for the Fast Light Tool Kit (FLTK).
  6. //
  7. // Copyright 1998-1999 by Bill Spitzak and others.
  8. //
  9. // This library is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU Library General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. //
  14. // This library is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17. // Library General Public License for more details.
  18. //
  19. // You should have received a copy of the GNU Library General Public
  20. // License along with this library; if not, write to the Free Software
  21. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  22. // USA.
  23. //
  24. // Please report all bugs and problems to "fltk-bugs@easysw.com".
  25. //
  26.  
  27. #include <FL/Fl.H>
  28. #include <FL/Fl_Window.H>
  29. #include <FL/x.H>
  30. #include <ctype.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33.  
  34. //
  35. // Globals...
  36. //
  37.  
  38. Fl_Widget    *Fl::belowmouse_,
  39.         *Fl::pushed_,
  40.         *Fl::focus_,
  41.         *Fl::selection_owner_;
  42. int        Fl::damage_,
  43.         Fl::e_x,
  44.         Fl::e_y,
  45.         Fl::e_x_root,
  46.         Fl::e_y_root,
  47.         Fl::e_state,
  48.         Fl::e_clicks,
  49.         Fl::e_is_click,
  50.         Fl::e_keysym;
  51. char        *Fl::e_text = "";
  52. int        Fl::e_length;
  53.  
  54. static double fl_elapsed();
  55.  
  56. //
  57. // 'Fl:event_inside()' - Return whether or not the mouse event is inside
  58. //                       the given rectangle.
  59. //
  60.  
  61. int Fl::event_inside(int x,int y,int w,int h) /*const*/ {
  62.   int mx = event_x() - x;
  63.   int my = event_y() - y;
  64.   return (mx >= 0 && mx < w && my >= 0 && my < h);
  65. }
  66.  
  67. int Fl::event_inside(const Fl_Widget *o) /*const*/ {
  68.   return event_inside(o->x(),o->y(),o->w(),o->h());
  69. }
  70.  
  71. // Timeouts are insert-sorted into order.  This works good if there
  72. // are only a small number:
  73.  
  74. static struct Timeout {
  75.   double time;
  76.   void (*cb)(void*);
  77.   void* arg;
  78. } * timeout;
  79. static int numtimeouts;
  80. static int timeout_array_size;
  81.  
  82. void Fl::add_timeout(double t, void (*cb)(void *), void *v) {
  83.  
  84.   fl_elapsed();
  85.  
  86.   if (numtimeouts >= timeout_array_size) {
  87.     timeout_array_size = 2*timeout_array_size+1;
  88.     timeout = (Timeout*)realloc(timeout, timeout_array_size*sizeof(Timeout));
  89.   }
  90.  
  91.   // insert-sort the new timeout:
  92.   int i;
  93.   for (i=0; i<numtimeouts; i++) {
  94.     if (timeout[i].time > t) {
  95.       for (int j=numtimeouts; j>i; j--) timeout[j] = timeout[j-1];
  96.       break;
  97.     }
  98.   }
  99.   timeout[i].time = t;
  100.   timeout[i].cb = cb;
  101.   timeout[i].arg = v;
  102.  
  103.   numtimeouts++;
  104. }
  105.  
  106. void Fl::remove_timeout(void (*cb)(void *), void *v) {
  107.   int i,j;
  108.   for (i=j=0; i<numtimeouts; i++) {
  109.     if (timeout[i].cb == cb && timeout[i].arg==v) ;
  110.     else {if (j<i) timeout[j]=timeout[i]; j++;}
  111.   }
  112.   numtimeouts = j;
  113. }
  114.  
  115. static int call_timeouts() {
  116.   int expired = 0;
  117.   while (numtimeouts) {
  118.     if (timeout[0].time > 0) break;
  119.     // we must remove timeout from array before doing the callback:
  120.     void (*cb)(void*) = timeout[0].cb;
  121.     void *arg = timeout[0].arg;
  122.     numtimeouts--; expired++;
  123.     if (numtimeouts) memmove(timeout, timeout+1, numtimeouts*sizeof(Timeout));
  124.     // now it is safe for the callback to do add_timeout:
  125.     cb(arg);
  126.   }
  127.   return expired;
  128. }
  129.  
  130. void Fl::flush() {
  131.   if (damage()) {
  132.     damage_ = 0;
  133.     for (Fl_X* x = Fl_X::first; x; x = x->next) {
  134.       if (x->w->damage() && x->w->visible()) {
  135.     if (x->wait_for_expose) {
  136.       // leave Fl::damage() set so programs can tell damage still exists
  137.       damage_ = 1;
  138.     } else {
  139.       x->flush();
  140.       x->w->clear_damage();
  141.     }
  142.       }
  143.     }
  144.   }
  145. #ifndef WIN32
  146.   if (fl_display) XFlush(fl_display);
  147. #endif
  148. }
  149.  
  150. extern double fl_wait(int timeout_flag, double timeout);
  151. extern int fl_ready();
  152.  
  153. static int initclock; // if false we didn't call fl_elapsed() last time
  154.  
  155. #ifndef WIN32
  156. #include <sys/time.h>
  157. #endif
  158.  
  159. // fl_elapsed must return the amount of time since the last time it was
  160. // called.  To reduce the number of system calls to get the
  161. // current time, the "initclock" symbol is turned on by an indefinite
  162. // wait.  This should then reset the measured-from time and return zero
  163. static double fl_elapsed() {
  164.  
  165. #ifdef WIN32
  166.  
  167.   unsigned long newclock = GetTickCount();
  168.   const int TICKS_PER_SECOND = 1000; // divisor of the value to get seconds
  169.   static unsigned long prevclock;
  170.   if (!initclock) {prevclock = newclock; initclock = 1; return 0.0;}
  171.   else if (newclock < prevclock) return 0.0;
  172.  
  173.   double t = double(newclock-prevclock)/TICKS_PER_SECOND;
  174.   prevclock = newclock;
  175.  
  176. #else
  177.  
  178.   static struct timeval prevclock;
  179.   struct timeval newclock;
  180.   gettimeofday(&newclock, NULL);
  181.   if (!initclock) {
  182.     prevclock.tv_sec = newclock.tv_sec;
  183.     prevclock.tv_usec = newclock.tv_usec;
  184.     initclock = 1;
  185.     return 0.0;
  186.   }
  187.   double t = newclock.tv_sec - prevclock.tv_sec +
  188.     (newclock.tv_usec - prevclock.tv_usec)/1000000.0;
  189.   prevclock.tv_sec = newclock.tv_sec;
  190.   prevclock.tv_usec = newclock.tv_usec;
  191.  
  192. #endif
  193.  
  194.   // expire any timeouts:
  195.   if (t > 0.0) for (int i=0; i<numtimeouts; i++) timeout[i].time -= t;
  196.   return t;
  197. }
  198.  
  199. void (*Fl::idle)();
  200. static char in_idle;
  201. static void callidle() {
  202.   if (!Fl::idle || in_idle) return;
  203.   in_idle = 1;
  204.   Fl::idle();
  205.   in_idle = 0;
  206. }
  207.  
  208. int Fl::wait() {
  209.   callidle();
  210.   int expired = 0;
  211.   if (numtimeouts) {fl_elapsed(); expired = call_timeouts();}
  212.   flush();
  213.   if (!Fl_X::first) return 0; // no windows
  214.   if ((idle && !in_idle) || expired) {
  215.     fl_wait(1,0.0);
  216.   } else if (numtimeouts) {
  217.     fl_wait(1, timeout[0].time);
  218.   } else {
  219.     initclock = 0;
  220.     fl_wait(0,0);
  221.   }
  222.   return 1;
  223. }
  224.  
  225. double Fl::wait(double time) {
  226.   callidle();
  227.   int expired = 0;
  228.   if (numtimeouts) {time -= fl_elapsed(); expired = call_timeouts();}
  229.   flush();
  230.   double wait_time = (idle && !in_idle) || expired ? 0.0 : time;
  231.   if (numtimeouts && timeout[0].time < wait_time) wait_time = timeout[0].time;
  232.   fl_wait(1, wait_time);
  233.   return time - fl_elapsed();
  234. }
  235.  
  236. int Fl::check() {
  237.   callidle();
  238.   if (numtimeouts) {fl_elapsed(); call_timeouts();}
  239.   fl_wait(1, 0.0);
  240.   flush();
  241.   return Fl_X::first != 0; // return true if there is a window
  242. }
  243.  
  244. int Fl::ready() {
  245.   // if (idle && !in_idle) return 1; // should it do this?
  246.   if (numtimeouts) {fl_elapsed(); if (timeout[0].time <= 0) return 1;}
  247.   return fl_ready();
  248. }
  249.  
  250. int Fl::run() {
  251.   while (wait());
  252.   return 0;
  253. }
  254.  
  255. ////////////////////////////////////////////////////////////////
  256. // Window list management:
  257.  
  258. Fl_X* Fl_X::first;
  259.  
  260. Fl_Window* fl_find(Window xid) {
  261.   Fl_X *window;
  262.   for (Fl_X **pp = &Fl_X::first; (window = *pp); pp = &window->next)
  263.     if (window->xid == xid) {
  264.       if (window != Fl_X::first && !Fl::modal()) {
  265.     // make this window be first to speed up searches
  266.     // this is not done if modal is true to avoid messing up modal stack
  267.     *pp = window->next;
  268.     window->next = Fl_X::first;
  269.     Fl_X::first = window;
  270.       }
  271.       return window->w;
  272.     }
  273.   return 0;
  274. }
  275.  
  276. void Fl::redraw() {
  277.   for (Fl_X* x = Fl_X::first; x; x = x->next) x->w->redraw();
  278. }
  279.  
  280. Fl_Window* Fl::first_window() {Fl_X* x = Fl_X::first; return x ? x->w : 0;}
  281.  
  282. Fl_Window* Fl::next_window(const Fl_Window* w) {
  283.   Fl_X* x = Fl_X::i(w)->next; return x ? x->w : 0;}
  284.  
  285. ////////////////////////////////////////////////////////////////
  286. // Event handlers:
  287.  
  288. struct handler_link {
  289.   int (*handle)(int);
  290.   const handler_link *next;
  291. };
  292.  
  293. static const handler_link *handlers = 0;
  294.  
  295. void Fl::add_handler(int (*h)(int)) {
  296.   handler_link *l = new handler_link;
  297.   l->handle = h;
  298.   l->next = handlers;
  299.   handlers = l;
  300. }
  301.  
  302. static int send_handlers(int event) {
  303.   for (const handler_link *h = handlers; h; h = h->next)
  304.     if (h->handle(event)) return 1;
  305.   return 0;
  306. }
  307.  
  308. ////////////////////////////////////////////////////////////////
  309.  
  310. Fl_Widget* fl_oldfocus; // kludge for Fl_Group...
  311.  
  312. void Fl::focus(Fl_Widget *o) {
  313.   if (grab()) return; // don't do anything while grab is on
  314.   Fl_Widget *p = focus_;
  315.   if (o != p) {
  316.     focus_ = o;
  317.     fl_oldfocus = 0;
  318.     for (; p && !p->contains(o); p = p->parent()) {
  319.       p->handle(FL_UNFOCUS);
  320.       fl_oldfocus = p;
  321.     }
  322.   }
  323. }
  324.  
  325. void Fl::belowmouse(Fl_Widget *o) {
  326.   if (grab()) return; // don't do anything while grab is on
  327.   Fl_Widget *p = belowmouse_;
  328.   if (o != p) {
  329.     event_is_click(0);
  330.     belowmouse_ = o;
  331.     for (; p && !p->contains(o); p = p->parent()) p->handle(FL_LEAVE);
  332.   }
  333. }
  334.  
  335. void Fl::pushed(Fl_Widget *o) {
  336.   pushed_ = o;
  337. }
  338.  
  339. Fl_Window *fl_xfocus;    // which window X thinks has focus
  340. Fl_Window *fl_xmousewin;// which window X thinks has FL_ENTER
  341. Fl_Window *Fl::grab_;    // most recent Fl::grab()
  342. Fl_Window *Fl::modal_;    // topmost modal() window
  343.  
  344. // Update modal(), focus() and other state according to system state,
  345. // and send FL_ENTER, FL_LEAVE, FL_FOCUS, and/or FL_UNFOCUS events.
  346. // This is the only function that produces these events in response
  347. // to system activity.
  348. // This is called whenever a window is added or hidden, and whenever
  349. // X says the focus or mouse window have changed.
  350.  
  351. void fl_fix_focus() {
  352.  
  353.   if (Fl::grab()) return; // don't do anything while grab is on.
  354.  
  355.   // set focus based on Fl::modal() and fl_xfocus
  356.   Fl_Widget* w = fl_xfocus;
  357.   if (w) {
  358.     while (w->parent()) w = w->parent();
  359.     if (Fl::modal()) w = Fl::modal();
  360.     if (!w->contains(Fl::focus()))
  361.       if (!w->take_focus()) Fl::focus(w);
  362.   } else
  363.     Fl::focus(0);
  364.  
  365.   if (!Fl::pushed()) {
  366.  
  367.     // set belowmouse based on Fl::modal() and fl_xmousewin:
  368.     w = fl_xmousewin;
  369.     if (w) {
  370.       if (Fl::modal()) w = Fl::modal();
  371.       if (!w->contains(Fl::belowmouse())) {
  372.     Fl::belowmouse(w);
  373.     w->handle(FL_ENTER);
  374.       } else {
  375.     // send a FL_MOVE event so the enter/leave state is up to date
  376.     Fl::e_x = Fl::e_x_root-fl_xmousewin->x();
  377.     Fl::e_y = Fl::e_y_root-fl_xmousewin->y();
  378.     w->handle(FL_MOVE);
  379.       }
  380.     } else {
  381.       Fl::belowmouse(0);
  382.     }
  383.   }
  384. }
  385.  
  386. #ifndef WIN32
  387. Fl_Widget *fl_selection_requestor; // from Fl_cutpaste.C
  388. #endif
  389.  
  390. // This function is called by ~Fl_Widget() and by Fl_Widget::deactivate
  391. // and by Fl_Widget::hide().  It indicates that the widget does not want
  392. // to receive any more events, and also removes all global variables that
  393. // point at the widget.
  394. // I changed this from the 1.0.1 behavior, the older version could send
  395. // FL_LEAVE or FL_UNFOCUS events to the widget.  This appears to not be
  396. // desirable behavior and caused flwm to crash.
  397.  
  398. void fl_throw_focus(Fl_Widget *o) {
  399.   if (o->contains(Fl::pushed())) Fl::pushed_ = 0;
  400.   if (o->contains(Fl::selection_owner())) Fl::selection_owner_ = 0;
  401. #ifndef WIN32
  402.   if (o->contains(fl_selection_requestor)) fl_selection_requestor = 0;
  403. #endif
  404.   if (o->contains(Fl::belowmouse())) Fl::belowmouse_ = 0;
  405.   if (o->contains(Fl::focus())) Fl::focus_ = 0;
  406.   if (o == fl_xfocus) fl_xfocus = 0;
  407.   if (o == fl_xmousewin) fl_xmousewin = 0;
  408.   fl_fix_focus();
  409. }
  410.  
  411. ////////////////////////////////////////////////////////////////
  412.  
  413. // Call to->handle but first replace the mouse x/y with the correct
  414. // values to account for nested X windows. 'window' is the outermost
  415. // window the event was posted to by X:
  416. static int send(int event, Fl_Widget* to, Fl_Window* window) {
  417.   int dx = window->x();
  418.   int dy = window->y();
  419.   for (const Fl_Widget* w = to; w; w = w->parent())
  420.     if (w->type()>=FL_WINDOW) {dx -= w->x(); dy -= w->y();}
  421.   int save_x = Fl::e_x; Fl::e_x += dx;
  422.   int save_y = Fl::e_y; Fl::e_y += dy;
  423.   int ret = to->handle(event);
  424.   Fl::e_y = save_y;
  425.   Fl::e_x = save_x;
  426.   return ret;
  427. }
  428.  
  429. int Fl::handle(int event, Fl_Window* window)
  430. {
  431.   Fl_Widget* w = window;
  432.  
  433.   switch (event) {
  434.  
  435.   case FL_CLOSE:
  436.     if (grab() || modal() && window != modal()) return 0;
  437.     w->do_callback();
  438.     return 1;
  439.  
  440.   case FL_SHOW:
  441.     ((Fl_Widget*)w)->show();
  442.     return 1;
  443.  
  444.   case FL_HIDE:
  445.     ((Fl_Widget*)w)->hide();
  446.     return 1;
  447.  
  448.   case FL_PUSH:
  449.     if (grab()) w = grab();
  450.     else if (modal() && w != modal()) return 0;
  451.     pushed_ = w;
  452.     if (send(event, w, window)) return 1;
  453.     // raise windows that are clicked on:
  454.     window->show();
  455.     return 1;
  456.  
  457.   case FL_MOVE:
  458.   case FL_DRAG:
  459.     fl_xmousewin = window; // this should already be set, but just in case.
  460.     if (pushed()) {
  461.       w = pushed();
  462.       event = FL_DRAG;
  463.     } else if (modal() && w != modal()) {
  464.       w = 0;
  465.     }
  466.     if (grab()) w = grab();
  467.     break;
  468.  
  469.   case FL_RELEASE: {
  470.     if (pushed()) {
  471.       w = pushed();
  472.       pushed_ = 0; // must be zero before callback is done!
  473.     }
  474.     if (grab()) w = grab();
  475.     int r = send(event, w, window);
  476.     fl_fix_focus();
  477.     return r;}
  478.  
  479.   case FL_UNFOCUS:
  480.     window = 0;
  481.   case FL_FOCUS:
  482.     fl_xfocus = window;
  483.     e_keysym = 0; // make sure it is not confused with navigation key
  484.     fl_fix_focus();
  485.     return 1;
  486.  
  487.   case FL_KEYBOARD:
  488.     fl_xfocus = window; // this should already be set, but just in case.
  489.  
  490.     // Try it as keystroke, sending it to focus and all parents:
  491.     for (w = grab() ? grab() : focus(); w; w = w->parent())
  492.       if (send(FL_KEYBOARD, w, window)) return 1;
  493.  
  494.     // recursive call to try shortcut:
  495.     if (handle(FL_SHORTCUT, window)) return 1;
  496.  
  497.     // and then try a shortcut with the case of the text swapped, by
  498.     // changing the text and falling through to FL_SHORTCUT case:
  499.     if (!isalpha(event_text()[0])) return 0;
  500.     *(char*)(event_text()) ^= ('A'^'a');
  501.     event = FL_SHORTCUT;
  502.  
  503.   case FL_SHORTCUT:
  504.  
  505.     if (grab()) {w = grab(); break;} // send it to grab window
  506.  
  507.     // Try it as shortcut, sending to mouse widget and all parents:
  508.     w = belowmouse(); if (!w) {w = modal(); if (!w) w = window;}
  509.     for (; w; w = w->parent()) if (send(FL_SHORTCUT, w, window)) return 1;
  510.  
  511.     // try using add_handle() functions:
  512.     if (send_handlers(FL_SHORTCUT)) return 1;
  513.  
  514.     // make Escape key close windows:
  515.     if (event_key()==FL_Escape) {
  516.       w = modal(); if (!w) w = window;
  517.       w->do_callback();
  518.       return 1;
  519.     }
  520.  
  521.     return 0;
  522.  
  523.   case FL_ENTER:
  524.     fl_xmousewin = window;
  525.     fl_fix_focus();
  526.     return 1;
  527.  
  528.   case FL_LEAVE:
  529.     if (window == fl_xmousewin) {fl_xmousewin = 0; fl_fix_focus();}
  530.     return 1;
  531.  
  532.   default:
  533.     break;
  534.   }
  535.   if (w && send(event, w, window)) return 1;
  536.   return send_handlers(event);
  537. }
  538.  
  539. ////////////////////////////////////////////////////////////////
  540. // hide() destroys the X window, it does not do unmap!
  541.  
  542. void Fl_Window::hide() {
  543.   clear_visible();
  544.   if (!shown()) return;
  545.  
  546.   // remove from the list of windows:
  547.   Fl_X* x = i;
  548.   Fl_X** pp = &Fl_X::first;
  549.   for (; *pp != x; pp = &(*pp)->next) if (!*pp) return;
  550.   *pp = x->next;
  551.   i = 0;
  552.  
  553.   // recursively remove any subwindows:
  554.   for (Fl_X *w = Fl_X::first; w;) {
  555.     Fl_Window* W = w->w;
  556.     if (W->window() == this) {
  557.       W->hide();
  558.       W->set_visible();
  559.       w = Fl_X::first;
  560.     } else w = w->next;
  561.   }
  562.  
  563.   if (this == Fl::modal_) { // we are closing the modal window, find next one:
  564.     Fl_Window* w;
  565.     for (w = Fl::first_window(); w; w = Fl::next_window(w))
  566.       if (w->modal()) break;
  567.     Fl::modal_ = w;
  568.   }
  569.  
  570.   // Make sure no events are sent to this window:
  571.   fl_throw_focus(this);
  572.   handle(FL_HIDE);
  573.  
  574. #ifdef WIN32
  575.   if (x->private_dc) ReleaseDC(x->xid,x->private_dc);
  576.   if (x->xid == fl_window) fl_GetDC(0); // releases dc belonging to window
  577. #else
  578.   if (x->region) XDestroyRegion(x->region);
  579. #endif
  580.   XDestroyWindow(fl_display, x->xid);
  581.  
  582.   delete x;
  583. }
  584.  
  585. Fl_Window::~Fl_Window() {
  586.   hide();
  587. }
  588.  
  589. // Child windows must respond to FL_SHOW and FL_HIDE by actually
  590. // doing unmap operations.  Outer windows assumme FL_SHOW & FL_HIDE
  591. // are messages from X:
  592.  
  593. int Fl_Window::handle(int event) {
  594.   if (parent()) switch (event) {
  595.   case FL_SHOW:
  596.     if (!shown()) show();
  597.     else XMapWindow(fl_display, fl_xid(this));
  598.     break;
  599.   case FL_HIDE:
  600.     if (shown()) XUnmapWindow(fl_display, fl_xid(this));
  601.     break;
  602.   }
  603.   return Fl_Group::handle(event);
  604. }
  605.  
  606. ////////////////////////////////////////////////////////////////
  607. // ~Fl_Widget() calls this: this function must get rid of any
  608. // global pointers to the widget.  This is also called by hide()
  609. // and deactivate().
  610.  
  611. // call this to free a selection (or change the owner):
  612. void Fl::selection_owner(Fl_Widget *owner) {
  613.   if (selection_owner_ && owner != selection_owner_)
  614.     selection_owner_->handle(FL_SELECTIONCLEAR);
  615.   if (focus_ && owner != focus_ && focus_ != selection_owner_)
  616.     focus_->handle(FL_SELECTIONCLEAR); // clear non-X-selection highlight
  617.   selection_owner_ = owner;
  618. }
  619.  
  620. #include <FL/fl_draw.H>
  621.  
  622. void Fl_Widget::redraw() {damage(FL_DAMAGE_ALL);}
  623.  
  624. void Fl_Widget::damage(uchar flags) {
  625.   if (type() < FL_WINDOW) {
  626.     // damage only the rectangle covered by a child widget:
  627.     damage(flags, x(), y(), w(), h());
  628.   } else {
  629.     // damage entire window by deleting the region:
  630.     Fl_X* i = Fl_X::i((Fl_Window*)this);
  631.     if (!i) return; // window not mapped, so ignore it
  632.     if (i->region) {XDestroyRegion(i->region); i->region = 0;}
  633.     damage_ |= flags;
  634.     Fl::damage(FL_DAMAGE_CHILD);
  635.   }
  636. }
  637.  
  638. void Fl_Widget::damage(uchar flags, int X, int Y, int W, int H) {
  639.   Fl_Widget* window = this;
  640.   // mark all parent widgets between this and window with FL_DAMAGE_CHILD:
  641.   while (window->type() < FL_WINDOW) {
  642.     window->damage_ |= flags;
  643.     window = window->parent();
  644.     if (!window) return;
  645.     flags = FL_DAMAGE_CHILD;
  646.   }
  647.   Fl_X* i = Fl_X::i((Fl_Window*)window);
  648.   if (!i) return; // window not mapped, so ignore it
  649.  
  650.   if (X<=0 && Y<=0 && W>=window->w() && H>=window->h()) {
  651.     // if damage covers entire window delete region:
  652.     window->damage(flags);
  653.     return;
  654.   }
  655.  
  656.   // clip the damage to the window and quit if none:
  657.   if (X < 0) {W += X; X = 0;}
  658.   if (Y < 0) {H += Y; Y = 0;}
  659.   if (W > window->w()-X) W = window->w()-X;
  660.   if (H > window->h()-Y) H = window->h()-Y;
  661.   if (W <= 0 || H <= 0) return;
  662.  
  663.   if (window->damage()) {
  664.     // if we already have damage we must merge with existing region:
  665.     if (i->region) {
  666. #ifndef WIN32
  667.       XRectangle R;
  668.       R.x = X; R.y = Y; R.width = W; R.height = H;
  669.       XUnionRectWithRegion(&R, i->region, i->region);
  670. #else
  671.       Region R = XRectangleRegion(X, Y, W, H);
  672.       CombineRgn(i->region, i->region, R, RGN_OR);
  673.       XDestroyRegion(R);
  674. #endif
  675.     }
  676.     window->damage_ |= flags;
  677.   } else {
  678.     // create a new region:
  679.     if (i->region) XDestroyRegion(i->region);
  680.     i->region = XRectangleRegion(X,Y,W,H);
  681.     window->damage_ = flags;
  682.   }
  683.   Fl::damage(FL_DAMAGE_CHILD);
  684. }
  685.  
  686. void Fl_Window::flush() {
  687.   make_current();
  688. //if (damage() == FL_DAMAGE_EXPOSE && can_boxcheat(box())) fl_boxcheat = this;
  689.   fl_clip_region(i->region); i->region = 0;
  690.   draw();
  691. }
  692.  
  693. int fl_old_shortcut(const char* s) {
  694.   if (!s || !*s) return 0;
  695.   int n = 0;
  696.   if (*s == '#') {n |= FL_ALT; s++;}
  697.   if (*s == '+') {n |= FL_SHIFT; s++;}
  698.   if (*s == '^') {n |= FL_CTRL; s++;}
  699.   return n | *s;
  700. }
  701.  
  702. //
  703. // End of "$Id: Fl.cxx,v 1.24.2.12 1999/08/22 23:31:21 gustavo Exp $".
  704. //
  705.